/*
 * Copyright 2014 Alex Curran
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.amlcurran.showcaseview;

import com.github.amlcurran.showcaseview.targets.Target;
import ohos.aafwk.ability.Ability;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.text.TextForm;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.ResourceManager;
import ohos.global.resource.solidxml.Pattern;
import ohos.global.resource.solidxml.TypedAttribute;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;
import ohos.multimodalinput.event.TouchEvent;

import java.lang.reflect.Field;
import java.util.HashMap;

import static com.github.amlcurran.showcaseview.AnimationFactory.AnimationEndListener;
import static com.github.amlcurran.showcaseview.AnimationFactory.AnimationStartListener;

/**
 * A view which allows you to showcase areas of your app with an explanation.
 */
public class ShowcaseView extends DependentLayout
        implements Component.TouchEventListener, ShowcaseViewApi, Component.DrawTask {
    /**
     * 默认
     */
    public static final int UNDEFINED = -1;

    /**
     * 文字在showcaseView左边
     */
    public static final int LEFT_OF_SHOWCASE = 0;

    /**
     * 文字在showcaseView右边
     */
    public static final int RIGHT_OF_SHOWCASE = 2;

    /**
     * 文字在showcaseView上面
     */
    public static final int ABOVE_SHOWCASE = 1;

    /**
     * 文字在showcaseView下面
     */
    public static final int BELOW_SHOWCASE = 3;

    private static final int HOLO_BLUE = Color.getIntColor("#33B5E5");
    private static final String TITLETEXT_APPEARANCE = "sv_titleTextAppearance";
    private static final String DETAILTEXT_APPEARANCE = "sv_detailTextAppearance";
    private static final String BACKGROUND_COLOR = "sv_backgroundColor";
    private static final String BUTTON_TEXT = "sv_buttonText";
    private static final String SHOWCASE_COLOR = "sv_showcaseColor";
    private static final String TINTBUTTON_COLOR = "sv_tintButtonColor";

    private Button mEndButton;
    private final TextDrawer textDrawer;
    private ShowcaseDrawer showcaseDrawer;
    private final ShowcaseAreaCalculator showcaseAreaCalculator;
    private final AnimationFactory animationFactory;
    private final ShotStateStore shotStateStore;

    // Showcase metrics
    protected int showcaseX = -1;
    protected int showcaseY = -1;
    private float scaleMultiplier = 1f;

    // Touch items
    private boolean hasCustomClickListener = false;
    private boolean blockTouches = true;
    private boolean hideOnTouch = false;
    private OnShowcaseEventListener mEventListener = OnShowcaseEventListener.NONE;

    private boolean hasAlteredText = false;
    private boolean hasNoTarget = false;
    private boolean shouldCentreText;
    private PixelMap bitmapBuffer;

    // Animation items
    private long fadeInMillis;
    private long fadeOutMillis;
    private int backgroundColor;
    private int showcaseColor;
    private boolean blockAllTouches;
    private ResourceManager mResourManager;


    public ShowcaseView(Context context, boolean newStyle) {
        this(context, null, null, newStyle);
    }

    public ShowcaseView(Context context, AttrSet attrSet, boolean newStyle) {
        this(context, attrSet, null, newStyle);
    }


    public ShowcaseView(Context context, AttrSet attrSet, String styleName, boolean newStyle) {
        super(context, attrSet, styleName);
        animationFactory = new AnimatorAnimationFactory();
        showcaseAreaCalculator = new ShowcaseAreaCalculator();
        shotStateStore = new ShotStateStore(context);
        mResourManager = context.getResourceManager();
        Pattern pattern = null;
        try {
            pattern = mResourManager.getElement(ResourceTable.Pattern_ShowcaseView).getPattern();
        } catch (Exception e) {
            LocalHiLog.warn("ShowcaseView", "ShowcaseView pattern not found");
        }

        // Set the default animation times
        fadeInMillis = 400;
        fadeOutMillis = 400;

        mEndButton = (Button) LayoutScatter.getInstance(context).
                parse(ResourceTable.Layout_showcase_button, null, false);
        if (newStyle) {
            showcaseDrawer = new NewShowcaseDrawer(context);
        } else {
            showcaseDrawer = new StandardShowcaseDrawer(context);
        }
        textDrawer = new TextDrawer(context);
        addDrawTask(this, DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
        init();
        updateStyle(pattern, false);
    }


    private void init() {
        setTouchEventListener(this);
        ComponentContainer.LayoutConfig layoutConfig = new
                ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,
                ComponentContainer.LayoutConfig.MATCH_PARENT);
        setLayoutConfig(layoutConfig);
        if (mEndButton.getComponentParent() == null) {
            int margin = 0;
            try {
                margin = (int) mResourManager.getElement(ResourceTable.Float_button_margin).getFloat();
            } catch (Exception e) {
                LocalHiLog.warn("ShowcaseView", "button_margin not found");
            }
            DependentLayout.LayoutConfig lps = new LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT);
            lps.addRule(LayoutConfig.ALIGN_PARENT_BOTTOM);
            lps.addRule(LayoutConfig.ALIGN_PARENT_RIGHT);
            lps.setMargins(margin, margin, margin, margin);

            mEndButton.setText("ok");
            if (!hasCustomClickListener) {
                mEndButton.setClickedListener(hideOnClickListener);
            }
            addComponent(mEndButton, lps);
        }
    }

    private boolean hasShot() {
        return shotStateStore.hasShot();
    }

    void setShowcasePosition(Point point) {
        setShowcasePosition(point.getPointXToInt(), point.getPointYToInt());
    }

    /**
     * 设置showcaseView位置
     *
     * @param x x
     * @param y y
     */
    void setShowcasePosition(int x, int y) {
        if (shotStateStore.hasShot()) {
            return;
        }
        Rect rect = getComponentPosition();
        showcaseX = x - rect.left;
        showcaseY = y - rect.top;
        recalculateText();
        invalidate();
    }

    /**
     * 设置需要展示的部分
     *
     * @param target target
     */
    public void setTarget(final Target target) {
        setShowcase(target, false);
    }

    /**
     * 设置展示部位
     *
     * @param target  target
     * @param animate 是否需要动画
     */
    public void setShowcase(final Target target, final boolean animate) {
        new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
            @Override
            public void run() {
                if (!shotStateStore.hasShot()) {
                    if (canUpdateBitmap()) {
                        updateBitmap();
                    }
                    Point targetPoint = target.getPoint();
                    if (targetPoint != null) {
                        hasNoTarget = false;
                        if (animate) {
                            animationFactory.animateTargetToPoint(ShowcaseView.this, targetPoint);
                        } else {
                            setShowcasePosition(targetPoint);
                        }
                    } else {
                        hasNoTarget = true;
                        invalidate();
                    }
                }
            }
        }, 100);
    }

    private void updateBitmap() {
        if (bitmapBuffer == null || haveBoundsChanged()) {
            if (bitmapBuffer != null) {
                bitmapBuffer.release();
            }
            PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
            initializationOptions.size = new Size(this.getWidth(), this.getHeight());
            initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
            initializationOptions.editable = true;
            bitmapBuffer = PixelMap.create(initializationOptions);
        }
    }

    private boolean haveBoundsChanged() {
        return getWidth() != bitmapBuffer.getImageInfo().size.width ||
                getHeight() != bitmapBuffer.getImageInfo().size.height;
    }

    /**
     * 判断是否有展示区域
     *
     * @return boolean
     */
    public boolean hasShowcaseView() {
        return (showcaseX != 1000000 && showcaseY != 1000000) && !hasNoTarget;
    }

    /**
     * 设置展示区域的X坐标
     *
     * @param positionX x坐标
     */
    public void setShowcaseX(int positionX) {
        setShowcasePosition(positionX, getShowcaseY());
    }

    /**
     * 设置展示区域的Y坐标
     *
     * @param positionY y坐标
     */
    public void setShowcaseY(int positionY) {
        setShowcasePosition(getShowcaseX(), positionY);
    }

    /**
     * 获取展示区域的x坐标
     *
     * @return x坐标
     */
    public int getShowcaseX() {
        Rect rect = getComponentPosition();
        return showcaseX + rect.left;
    }

    /**
     * 获取展示区域的y坐标
     *
     * @return y坐标
     */
    public int getShowcaseY() {
        Rect rect = getComponentPosition();
        return showcaseY + rect.top;
    }

    /**
     * Override the standard button click event
     *
     * @param listener Listener to listen to on click events
     */
    public void overrideButtonClick(ClickedListener listener) {
        if (shotStateStore.hasShot()) {
            return;
        }
        if (mEndButton != null) {
            if (listener != null) {
                mEndButton.setClickedListener(listener);
            } else {
                mEndButton.setClickedListener(hideOnClickListener);
            }
        }
        hasCustomClickListener = true;
    }

    /**
     * 设置ShowcaseView显示和隐藏的监听事件
     *
     * @param listener listener
     */
    public void setOnShowcaseEventListener(OnShowcaseEventListener listener) {
        if (listener != null) {
            mEventListener = listener;
        } else {
            mEventListener = OnShowcaseEventListener.NONE;
        }
    }

    /**
     * 设置按钮文字
     *
     * @param text 文字
     */
    public void setButtonText(String text) {
        if (mEndButton != null) {
            mEndButton.setText(text);
        }
    }

    private void recalculateText() {
        boolean recalculatedCling = showcaseAreaCalculator.calculateShowcaseRect(showcaseX, showcaseY, showcaseDrawer);
        boolean recalculateText = recalculatedCling || hasAlteredText;
        if (recalculateText) {
            Rect rect = hasShowcaseView() ? showcaseAreaCalculator.getShowcaseRect() : new Rect();
            textDrawer.calculateTextPosition(getWidth(), getHeight(), shouldCentreText, rect);
        }
        hasAlteredText = false;
    }


    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (showcaseX < 0 || showcaseY < 0 || shotStateStore.hasShot() || bitmapBuffer == null || !this.isShowing()) {
            return;
        }

        // Draw background color
        showcaseDrawer.erase(bitmapBuffer);

        // Draw the showcase drawable
        if (!hasNoTarget) {
            showcaseDrawer.drawShowcase(bitmapBuffer, showcaseX, showcaseY, scaleMultiplier);
        }
        showcaseDrawer.drawToCanvas(canvas, bitmapBuffer);
        if (!hasNoTarget) {
            showcaseDrawer.drawShowcaseBorder(canvas, showcaseX, showcaseY);
        }
        // Draw the text on the screen, recalculating its position if necessary
        textDrawer.draw(canvas);
    }


    @Override
    public void hide() {
        // If the type is set to one-shot, store that it has shot
        shotStateStore.storeShot();
        mEventListener.onShowcaseViewHide(this);
        fadeOutShowcase();
    }


    private void clearBitmap() {
        if (bitmapBuffer != null && !bitmapBuffer.isReleased()) {
            bitmapBuffer.release();
            bitmapBuffer = null;
        }
    }

    private void fadeOutShowcase() {
        animationFactory.fadeOutView(
                ShowcaseView.this, fadeOutMillis, new AnimationEndListener() {
                    @Override
                    public void onAnimationEnd() {
                        ShowcaseView.this.setVisibility(Component.HIDE);
                        mEventListener.onShowcaseViewDidHide(ShowcaseView.this);
                    }
                }
        );
    }

    @Override
    public void show() {
        mEventListener.onShowcaseViewShow(this);
        fadeInShowcase();
    }

    @Override
    public void destory() {
        clearBitmap();
    }

    private boolean canUpdateBitmap() {
        return getHeight() > 0 && getWidth() > 0;
    }

    private void fadeInShowcase() {
        animationFactory.fadeInView(
                ShowcaseView.this, fadeInMillis,
                new AnimationStartListener() {
                    @Override
                    public void onAnimationStart() {
                        setVisibility(Component.VISIBLE);
                    }
                }
        );
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (blockAllTouches) {
            mEventListener.onShowcaseViewTouchBlocked(touchEvent);
            return true;
        }

        float xDelta = Math.abs(EventUtil.getRawX(touchEvent) - showcaseX);
        float yDelta = Math.abs(EventUtil.getRawY(touchEvent) - showcaseY);
        double distanceFromFocus = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2));

        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP &&
                hideOnTouch && distanceFromFocus > showcaseDrawer.getBlockedRadius()) {
            this.hide();
            return true;
        }

        boolean blocked = blockTouches && distanceFromFocus > showcaseDrawer.getBlockedRadius();
        if (blocked) {
            mEventListener.onShowcaseViewTouchBlocked(touchEvent);
        }
        return blocked;
    }


    private static void insertShowcaseView(ShowcaseView showcaseView, ComponentContainer parent, int parentIndex) {
        parent.addComponent(showcaseView, parentIndex);
        if (!showcaseView.hasShot()) {
            showcaseView.show();
        } else {
            showcaseView.hideImmediate();
        }
    }

    private void hideImmediate() {
        setVisibility(Component.HIDE);
    }

    @Override
    public void setContentTitle(String title) {
        textDrawer.setContentTitle(title);
        invalidate();
    }

    @Override
    public void setContentText(String text) {
        textDrawer.setContentText(text);
        invalidate();
    }

    private void setScaleMultiplier(float scaleMultiplier) {
        this.scaleMultiplier = scaleMultiplier;
    }

    /**
     * 隐藏底部按钮
     */
    public void hideButton() {
        mEndButton.setVisibility(Component.HIDE);
    }

    /**
     * 显示底部按钮
     */
    public void showButton() {
        mEndButton.setVisibility(VISIBLE);
    }

    /**
     * Builder class which allows easier creation of {@link ShowcaseView}s.
     * It is recommended that you use this Builder class.
     */
    public static class Builder {
        private ShowcaseView showcaseView;
        private Ability activity;
        private ComponentContainer parent;
        private int parentIndex;

        /**
         * 构造函数
         *
         * @param activity ability
         * @param parent   根布局
         */
        public Builder(Ability activity, ComponentContainer parent) {
            this(activity, parent, false);
        }


        /**
         * 构造函数
         *
         * @param activity    ability
         * @param parent      根布局
         * @param useNewStyle should use "new style" showcase (see {@link #withNewStyleShowcase()}
         */
        @Deprecated
        public Builder(Ability activity, ComponentContainer parent, boolean useNewStyle) {
            this.activity = activity;
            this.showcaseView = new ShowcaseView(activity, useNewStyle);
            this.showcaseView.setTarget(Target.NONE);
            this.parent = parent;
            this.parentIndex = this.parent.getChildCount();
        }

        /**
         * Create the {@link com.github.amlcurran.showcaseview.ShowcaseView} and show it.
         *
         * @return the created ShowcaseView
         */
        public ShowcaseView build() {
            insertShowcaseView(showcaseView, parent, parentIndex);
            return showcaseView;
        }

        /**
         * Draw a holo-style showcase. This is the default.<br/>
         * <img alt="Holo showcase example" src="../../../../../../../../example2.png" />
         *
         * @return Builder
         */
        public Builder withHoloShowcase() {
            return setShowcaseDrawer(new StandardShowcaseDrawer(activity));
        }

        /**
         * Draw a new-style showcase.<br/>
         * <img alt="Holo showcase example" src="../../../../../../../../example.png" />
         *
         * @return Builder
         */
        public Builder withNewStyleShowcase() {
            return setShowcaseDrawer(new NewShowcaseDrawer(activity));
        }

        /**
         * Draw a material style showcase.
         * <img alt="Material showcase" src="../../../../../../../../material.png" />
         *
         * @return Builder
         */
        public Builder withMaterialShowcase() {
            return setShowcaseDrawer(new MaterialShowcaseDrawer(activity));
        }


        /**
         * Set a custom showcase drawer which will be responsible for measuring and drawing the showcase
         *
         * @param showcaseDrawer showcaseDrawer
         * @return Builder
         */
        public Builder setShowcaseDrawer(ShowcaseDrawer showcaseDrawer) {
            showcaseView.setShowcaseDrawer(showcaseDrawer);
            return this;
        }


        /**
         * Set the title text shown on the ShowcaseView.
         *
         * @param resId 描述内容Id
         * @return Builder
         */
        public Builder setContentTitle(int resId) {
            String title = "";
            try {
                title = activity.getResourceManager().getElement(resId).getString();
            } catch (Exception e) {
                LocalHiLog.warn("ShowcaseView", "title not found");
            }
            return setContentTitle(title);
        }


        /**
         * Set the title text shown on the ShowcaseView.
         *
         * @param title 设置标题
         * @return Builder
         */
        public Builder setContentTitle(String title) {
            showcaseView.setContentTitle(title);
            return this;
        }

        /**
         * Set the descriptive text shown on the ShowcaseView.
         *
         * @param resId 描述内容资源Id
         * @return Builder
         */
        public Builder setContentText(int resId) {
            String text = "";
            try {
                text = activity.getResourceManager().getElement(resId).getString();
            } catch (Exception e) {
                LocalHiLog.warn("ShowcaseView", "text not found");
            }
            return setContentText(text);
        }


        /**
         * Set the descriptive text shown on the ShowcaseView.
         *
         * @param text 描述内容
         * @return Builder
         */
        public Builder setContentText(String text) {
            showcaseView.setContentText(text);
            return this;
        }

        /**
         * Set the target of the showcase.
         *
         * @param target a {@link com.github.amlcurran.showcaseview.targets.Target} representing
         *               the item to showcase (e.g., a button, or action item).
         * @return Builder
         */
        public Builder setTarget(Target target) {
            showcaseView.setTarget(target);
            return this;
        }


        /**
         * Set the style of the ShowcaseView. See the sample app for example styles.
         *
         * @param theme 设置pattern的id
         * @return Builder
         */
        public Builder setStyle(int theme) {
            showcaseView.setStyle(theme);
            return this;
        }

        /**
         * Set a listener which will override the button clicks.
         * <p/>
         * Note that you will have to manually hide the ShowcaseView
         *
         * @param onClickListener 点击事件
         * @return Builder
         */
        public Builder setOnClickListener(ClickedListener onClickListener) {
            showcaseView.overrideButtonClick(onClickListener);
            return this;
        }


        /**
         * Don't make the ShowcaseView block touches on itself. This doesn't
         * block touches in the showcased area.
         * <p/>
         * By default, the ShowcaseView does block touches
         *
         * @return Builder
         */
        public Builder doNotBlockTouches() {
            showcaseView.setBlocksTouches(false);
            return this;
        }

        /**
         * Make this ShowcaseView hide when the user touches outside the showcased area.
         * This enables {@link #doNotBlockTouches()} as well.
         * <p/>
         * By default, the ShowcaseView doesn't hide on touch.
         *
         * @return bulid
         */
        public Builder hideOnTouchOutside() {
            showcaseView.setBlocksTouches(true);
            showcaseView.setHideOnTouchOutside(true);
            return this;
        }

        /**
         * Set the ShowcaseView to only ever show once.
         *
         * @param shotId a unique identifier (<em>across the app</em>) to store
         *               whether this ShowcaseView has been shown.
         * @return bulid
         */
        public Builder singleShot(long shotId) {
            showcaseView.setSingleShot(shotId);
            return this;
        }

        /**
         * 设置触摸事件
         *
         * @param showcaseEventListener 接口事件
         * @return Builder
         */
        public Builder setShowcaseEventListener(OnShowcaseEventListener showcaseEventListener) {
            showcaseView.setOnShowcaseEventListener(showcaseEventListener);
            return this;
        }

        /**
         * 设置根布局
         *
         * @param parent 根布局
         * @param index  放在什么位置
         * @return Builder
         */
        public Builder setParent(ComponentContainer parent, int index) {
            this.parent = parent;
            this.parentIndex = index;
            return this;
        }


        /**
         * Sets the paint that will draw the text as specified by {@link #setContentText(String)}
         * or {@link #setContentText(int)}. If you're using a TextAppearance (set by {@link #setStyle(int)},
         * then this {@link TextForm} will override that TextAppearance.
         *
         * @param textTextForm 描述文字样式
         * @return Builder
         */
        public Builder setContentTextForm(TextForm textTextForm) {
            showcaseView.setContentTextForm(textTextForm);
            return this;
        }


        /**
         * Sets the paint that will draw the text as specified by {@link #setContentTitle(String)}
         * or {@link #setContentTitle(int)}. If you're using a TextAppearance (set by {@link #setStyle(int)},
         * then this {@link TextForm} will override that TextAppearance.
         *
         * @param titleTextForm 标题样式
         * @return Builder
         */
        public Builder setContentTitlTextForm(TextForm titleTextForm) {
            showcaseView.setContentTitleTextForm(titleTextForm);
            return this;
        }


        /**
         * Replace the end button with the one provided. Note that this resets any OnClickListener provided
         * by {@link #setClickedListener(ClickedListener)}, so call this method before that one.
         *
         * @param button 按钮
         * @return Builder
         */
        public Builder replaceEndButton(Button button) {
            showcaseView.setEndButton(button);
            return this;
        }


        /**
         * Replace the end button with the one provided. Note that this resets any OnClickListener provided
         * by {@link #setClickedListener(ClickedListener)}, so call this method before that one.
         *
         * @param buttonResourceId 按钮Id
         * @return Builder
         */
        public Builder replaceEndButton(int buttonResourceId) {
            Component view = LayoutScatter.getInstance(activity).parse(buttonResourceId, showcaseView, false);
            if (!(view instanceof Button)) {
                throw new IllegalArgumentException
                        ("Attempted to replace showcase button with a layout which isn't a button");
            }
            return replaceEndButton((Button) view);
        }

        /**
         * Block any touch made on the ShowcaseView, even inside the showcase
         *
         * @return Builder
         */
        public Builder blockAllTouches() {
            showcaseView.setBlockAllTouches(true);
            return this;
        }

    }

    private void setEndButton(Button button) {
        ComponentContainer.LayoutConfig copyParams = mEndButton.getLayoutConfig();
        mEndButton.setClickedListener(null);
        removeComponent(mEndButton);
        mEndButton = button;
        button.setClickedListener(hideOnClickListener);
        button.setLayoutConfig(copyParams);
        addComponent(button);
    }

    private void setShowcaseDrawer(ShowcaseDrawer showcaseDrawer) {
        this.showcaseDrawer = showcaseDrawer;
        this.showcaseDrawer.setBackgroundColour(backgroundColor);
        this.showcaseDrawer.setShowcaseColour(showcaseColor);
        hasAlteredText = true;
        invalidate();
    }

    private void setContentTitleTextForm(TextForm titleTextFoem) {
        this.textDrawer.setTitleTextForm(titleTextFoem);
        hasAlteredText = true;
        invalidate();
    }

    private void setContentTextForm(TextForm textTextFoem) {
        this.textDrawer.setContentTextForm(textTextFoem);
        hasAlteredText = true;
        invalidate();
    }


    /**
     * Set whether the text should be centred in the screen, or left-aligned (which is the default).
     *
     * @param shouldCentreText shouldCentreText
     */
    public void setShouldCentreText(boolean shouldCentreText) {
        this.shouldCentreText = shouldCentreText;
        hasAlteredText = true;
        invalidate();
    }


    private void setSingleShot(long shotId) {
        shotStateStore.setSingleShot(shotId);
    }

    /**
     * Change the position of the ShowcaseView's button from the default bottom-right position.
     *
     * @param layoutParams a {@link ohos.agp.components.DependentLayout.LayoutConfig} representing
     *                     the new position of the button
     */
    @Override
    public void setButtonPosition(LayoutConfig layoutParams) {
        mEndButton.setLayoutConfig(layoutParams);
    }

    /**
     * Sets the text alignment of the detail text
     *
     * @param textAlignment textAlignment
     */
    public void setDetailTextAlignment(TextAlignment textAlignment) {
        textDrawer.setDetailTextAlignment(textAlignment);
        hasAlteredText = true;
        invalidate();
    }

    /**
     * Sets the text alignment of the title text
     *
     * @param textAlignment textAlignment
     */
    public void setTitleTextAlignment(TextAlignment textAlignment) {
        textDrawer.setTitleTextAlignment(textAlignment);
        hasAlteredText = true;
        invalidate();
    }


    /**
     * Set the duration of the fading in and fading out of the ShowcaseView
     *
     * @param fadeInMillis  显示动画时间
     * @param fadeOutMillis 隐藏动画时间
     */
    public void setFadeDurations(long fadeInMillis, long fadeOutMillis) {
        this.fadeInMillis = fadeInMillis;
        this.fadeOutMillis = fadeOutMillis;
    }

    /**
     * 文字位置
     *
     * @param textPosition LEFT_OF_SHOWCASE ABOVE_SHOWCASE  RIGHT_OF_SHOWCASE  BELOW_SHOWCASE
     */
    public void forceTextPosition(int textPosition) {
        textDrawer.forceTextPosition(textPosition);
        hasAlteredText = true;
        invalidate();
    }


    @Override
    public void setHideOnTouchOutside(boolean hideOnTouch) {
        this.hideOnTouch = hideOnTouch;
    }


    @Override
    public void setBlocksTouches(boolean blockTouches) {
        this.blockTouches = blockTouches;
    }

    private void setBlockAllTouches(boolean blockAllTouches) {
        this.blockAllTouches = blockAllTouches;
    }


    @Override
    public void setStyle(int theme) {
        Pattern array = null;
        try {
            array = mResourManager.getElement(theme).getPattern();
            updateStyle(array, true);
        } catch (Exception e) {
            LocalHiLog.warn("ShowcaseView", "style Pattern not found");
        }
    }

    @Override
    public boolean isShowing() {
        return getVisibility() == Component.VISIBLE;
    }

    private void updateStyle(Pattern styled, boolean invalidate) {
        boolean tintButton = true;
        String buttonText = "OK";
        Pattern detailPattern = null;
        Pattern titlePattern = null;
        try {
            HashMap<String, TypedAttribute> patternHash = styled.getPatternHash();
            TypedAttribute attribute = patternHash.get(BACKGROUND_COLOR);
            if (attribute != null) {
                backgroundColor = Color.getIntColor(attribute.getStringValue());
            } else {
                backgroundColor = Color.argb(128, 80, 80, 80);
            }
            attribute = patternHash.get(SHOWCASE_COLOR);
            if (attribute != null) {
                showcaseColor = Color.getIntColor(attribute.getStringValue());
            } else {
                showcaseColor = HOLO_BLUE;
            }
            attribute = patternHash.get(BUTTON_TEXT);
            if (attribute != null) {
                buttonText = attribute.getStringValue();
            }
            attribute = patternHash.get(TINTBUTTON_COLOR);
            if (attribute != null) {
                tintButton = attribute.getBooleanValue();
            }
            attribute = patternHash.get(TITLETEXT_APPEARANCE);
            if (attribute != null) {
                String titleStyleName = attribute.getStringValue();
                titlePattern = mResourManager.getElement(getResourceId(titleStyleName)).getPattern();
            }
            attribute = patternHash.get(DETAILTEXT_APPEARANCE);
            if (attribute != null) {
                String detailStyleName = attribute.getStringValue();
                detailPattern = mResourManager.getElement(getResourceId(detailStyleName)).getPattern();
            }
        } catch (Exception e) {
            LocalHiLog.warn("ShowcaseView", "detailStyleName not found");
        }
        showcaseDrawer.setShowcaseColour(showcaseColor);
        showcaseDrawer.setBackgroundColour(backgroundColor);
        tintButton(showcaseColor, tintButton);
        mEndButton.setText(buttonText);
        textDrawer.setTitleStyling(titlePattern);
        textDrawer.setDetailStyling(detailPattern);
        hasAlteredText = true;

        if (invalidate) {
            invalidate();
        }
    }

    private int getResourceId(String name) {
        Class cls = null;
        int id = 0;
        try {
            cls = Class.forName(getContext().getBundleName() + ".ResourceTable");
            Field declaredField = cls.getField("Pattern_" + name);
            id = declaredField.getInt(null);
        } catch (Exception e) {
            LocalHiLog.warn("ShowcaseView", "not found Pattern id");
        }
        return id;
    }

    private void tintButton(int showcaseColor, boolean tintButton) {
        ShapeElement element = new ShapeElement();
        if (tintButton) {
            float[] rgba = ColorUtils.converArgbToRgb(showcaseColor);
            element.setRgbColor(new RgbColor((int) rgba[0], (int) rgba[1], (int) rgba[2], (int) rgba[3]));
            element.setStateColorMode(BlendMode.MULTIPLY);
            mEndButton.setBackground(element);
        } else {
            mEndButton.setBackground(null);
        }
    }


    private ClickedListener hideOnClickListener = new ClickedListener() {
        @Override
        public void onClick(Component component) {
            hide();
        }
    };

    /**
     * 文本对齐方式
     */
    public static enum TextAlignment {
        LEFT(ohos.agp.utils.TextAlignment.LEFT),
        RIGHT(ohos.agp.utils.TextAlignment.RIGHT),
        CENTER(ohos.agp.utils.TextAlignment.HORIZONTAL_CENTER);
        private int value;

        TextAlignment(int value) {
            this.value = value;
        }

        /**
         * 返回值
         *
         * @return int
         */
        public int value() {
            return value;
        }
    }


}
